/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package org.opmviewer.graphviz;

import java.awt.Shape;
import java.awt.geom.Rectangle2D;
import java.io.PrintWriter;
import java.io.StringReader;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.w3c.dom.Document;
import org.xml.sax.InputSource;
import org.xml.sax.XMLReader;
import prefuse.data.Edge;
import prefuse.data.Node;
import prefuse.visual.VisualGraph;

/**
 *
 * @author grzegorz
 * This class is used to translate a prefuse graph into DOT language,
 * send the description to Graphviz and then assign coordinates to nodes
 * based on Graphviz's response.
 *
 *
 */
public class GraphvizCoordinateFetcher {

    String indent = "  ";
    VisualGraph graph;
    String rankdir;
    String dot;
    String result;
    Document doc;
    Map<Integer, Shape> nodeShapes;
    Map<String, EdgeShape> edgeShapes;
    Map<Integer, Rectangle2D> nodeRects;

    public GraphvizCoordinateFetcher(VisualGraph graph) {
        this.graph = graph;
        nodeShapes = new HashMap<Integer, Shape>();
        edgeShapes = new HashMap<String, EdgeShape>();
        nodeRects = new HashMap<Integer, Rectangle2D>();
        //rankdir = (isTB) ? "  rankdir=TB;\n" : "  rankdir=LR;\n";
        System.out.println("Creating DOT");
        createDOT();
        System.out.println("Running Graphviz");
        runGraphviz();
        System.out.println("Reading document");
        read();
    }

    public Map<String, EdgeShape> getEdgeShapes() {
        return edgeShapes;
    }

    private void createDOT() {
        StringBuffer sb = new StringBuffer();
        sb.append("digraph G {\n");
        sb.append("rankdir=BT\n"
                + "node[fontsize=20, font=Tahoma]\n");
        //sb.append(rankdir);

        //listing all transition labels
        sb.append("\n");
        //sb.append(indent+"node [shape=box, fontname="+fn+"r, fontsize="+fs+"];\n");
        Iterator it = graph.nodes();
        while (it.hasNext()) {
            Node n = (Node) it.next();
            sb.append(indent + "{node [label=\"" + n.getString("LABEL") + "\""
                    + " shape=rect] "
                    + n.getInt("NODE_ID") + ";}\n");
        }
        //listing all edges

        it = graph.edges();

        while (it.hasNext()) {
            Edge e = (Edge) it.next();
            sb.append(indent + e.getSourceNode().getInt("NODE_ID")
                    + " -> " + e.getTargetNode().getInt("NODE_ID") + ";\n");
        }

        sb.append("}\n");
        dot = sb.toString();
    }

    private void runGraphviz() {
        Process gviz;
        try {
            gviz = Runtime.getRuntime().exec(new String[]{"dot", "-Tsvg"});
        } catch (Throwable ee) {
            ee.printStackTrace();
            return;
        }
        StreamDevourer devourer = new StreamDevourer(gviz.getInputStream());
        devourer.start();

        // Must create an error devourer otherwise stderr fills up and the
        // process stalls!
        StreamDevourer errorDevourer = new StreamDevourer(gviz.getErrorStream());
        errorDevourer.start();

        PrintWriter out = new PrintWriter(gviz.getOutputStream(), true);
        out.print(dot);
        out.flush();
        out.close();
        //String result = errorDevourer.blockOnOutput();
        result = devourer.blockOnOutput();
        System.err.println(result);

    }

    private void read() {
        try {
            SAXParserFactory spf = SAXParserFactory.newInstance();
            SAXParser sp = spf.newSAXParser();
            XMLReader xr = sp.getXMLReader();
            SVGGraphHandler handler = new SVGGraphHandler();

            xr.setEntityResolver(new DummyEntityResolver());
            xr.setContentHandler(handler);

            InputSource is = new InputSource(new StringReader(result));
            
            System.err.println("before parse");
            xr.parse(is);
            System.err.println("after parse");
            edgeShapes = handler.getEdgeShapes();
            nodeRects = handler.getNodeRects();
            graph.getNodes().addColumn("xc", double.class);
            graph.getNodes().addColumn("yc", double.class);
            graph.getNodes().addColumn("w", double.class);
            graph.getNodes().addColumn("h", double.class);
            Iterator it = graph.nodes();
            while (it.hasNext()) {
                Node n = (Node) it.next();
                Rectangle2D r2d = nodeRects.get(n.getInt("NODE_ID"));
                n.setDouble("xc", r2d.getX());
                n.setDouble("yc", r2d.getY());
                n.setDouble("w", r2d.getWidth());
                n.setDouble("h", r2d.getHeight());
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
